图书数据库的基地址
1 | # 基地址 |
截止2018你那9月11日,以上地址已经无法访问,此地址仅为项目演示地址
定义参数,判断查询参数q是否是isbn号
1 | @app.route("/search/q/<page>") |
要点解析
- 判断字符串是否是数字,可以使用函数
isdigit()
in
关键字可以判断一个字符串是否包含在另外一个字符串中- 多个逻辑循环原则:
- 尽量把判断结构为的假的放在前边,利用 逻辑判断的短路优势,可以减少资源消耗。
- 需要查询数据库的尽量放在后边。
简单重构
上面代码的缺点
- 判断代码太多,使得代码很臃肿。而且不利于以后代码的复用
- 代码可读性查,使后来者不能一眼看出主要功能。
重构后的代码
fisher.py
1 | @app.route("/search/<q>/<page>") |
helper.py
1 | def is_isbn_or_key(word): |
要点解析:
- 把判断 isbn 的代码隔离出去,新建
helper.py
文件,可以重复利用。
建议
其他地方的代码多一点还可以接受,视图函数中不可以,因为视图函数是一个web项目的入口。所有人阅读都是从这里入手,应该把细节屏蔽掉,给阅读者一个选择。
看源码的技巧:先通读整体,了解整体过程,再回过头来了解细节,不要从一开始就深究每一个细节
过多的注释会让代码变的臃肿,尽量使用易懂的函数名来代替注释,保持代码的简洁性
requests发送http请求及代码的简化手段
http.py
1 | class HTTP: |
知识点:
- 简化
if-else
语句的几种方式- 1.使用三元表达式 ;
- 2.
if+return
; - 3.将
if-else
里的代码提取成函数
if+return
的理解:- 把最后一句
return
前的if+return
全都理解为正常流程之外的一种特例情况的处理; - 多次
if-return
,提前结束一些逻辑分支,可以提高代码思维的清晰性
- 把最后一句
requests
的一些说明:- 1.
get()
发送get
请求; - 2.返回结果
r.status_code
获取返回状态吗; - 3.
r.json()
将返回结果序列化成json
; - 4.
r.text
将返回结果不做处理直接返回
- 1.
requests vs urllib
发送http请求的两种方法:
- 1.使用urllib(python内置)
- 2.使用requests(需要使用pip3安装)
urllib的用法:
urllib缺点分析
- 需要对url进行编码,并且有些字符不需要编码还要声明出来
- 返回的结果是字节码,需要包装成字符串
- 404的情况是通过异常的形式抛出,现在流行的restful,404已经不是一种异常了
从API获取数据
将具体调用HTTP
请求,获取结果的业务代码封装到`YuShuBook中
1 | class YuShuBook: |
使用 json.dumps
序列表返回结果,在视图函数中进行返回,并声明状态码和返回类型(一个元组)
1 | @app.route("/book/search/<q>/<page>") |
可以使用
flask
提供的jsonify
替换麻烦的json.dumps
和元组
将视图函数拆分到单独的模块中
1.将试图函数都放在一个文件中有哪些不足:
代码太长,不利于维护
从业务模型抽象的角度,不应该把他们都放在一个文件中。关于书籍相关的API就应该放在书籍模型的视图函数文件中,跟用户相关的API就应该放在用户模型相关的文件中
入口文件的意义比较独特,会启动web服务器以及做很多初始化的操作,就算要放在一个文件也不应该业务的操作放在入口文件中来
2.尝试拆分模块
思路
将试图函数抽离到单独的包中,然后在新的试图文件中引入flask.py来导入app核心对象。
为了新的试图文件中的路由可以成功注册,再在flask.py中引入刚刚抽离出的试图模块
修改后的fisher.py
1 | from flask import Flask |
新增的book.py
1 | from flask import jsonify |
但是这样做并不是正确的做法,结果表明,这样修改以后,访问
search api
会404为了知道为什么这样做不行,我们需要先刨铣一下
Flask路由机制
的原理
3.Flask路由机制
flask路由机制
flask的基本思想是内部会维护一个字典。
每一个
url
都会对应一个视图函数,但是不仅仅是这样。每一个
url
还会对应一个endpoint
端点。用于反向构建URL(后面会讲解)
flask的路由注册
app_url_rule(url=,view_func=,endpoint=)
会接受三个参数,前两个我们都知道了,第三个就是上面说的
endpoint
。他的默认值是view_func
的名称。当然,
app.route('url',endpoint=)
也可以传入
flask route的部分源码
1 | def route(self, rule, **options): |
通过端点调试可以发现,Flask内部由
url_map
维护一个url->endpoint
的指向。由
view_functions
记录endpoint
所指向视图函数的函数,这样请求进入到Flask内部,才能通过
Url
找到对应的视图函数
4. 循环引入流程分析
从上面的断点调试中发现,我们的
url_map
和view_functions
中都已经维护了相关的信息。但是为什么还是会出现404的情况,这是因为
fisher.py
和book.py
出现了循环引入的情况。
下面看下fisher.py
和book.py
的具体流程图
图中有两种颜色的线:红色的线是
fisher
主执行文件被执行之后的执行路径;蓝色的线是book模块被导入之后循环导入的执行路径。
- 1.主流程开始之后,首先到达
导入book
的语句。然后进入book
模块中执行 - 2.book模块开始之后,首先到达
导入fisher
的语句(循环导入),这个时候主流程暂时结束,重新执行fisher中的代码 - 3.这时候又回到fisher中的导入book的语句,由于book已经被导入一次,所以不会再次导入,进入if语句,这个时候的name是book导入fisher时候的name:fisher,不是主流程main,所以if语句条件为false。蓝色线执行终止,重新回到2. book导入fisher的语句。
- 4.继续向下执行book 中app.route注册路由的语句。然后book执行完,回到fisher主流程执行中。
- 5.到达if语句,这个时候name为main。执行run方法,启动服务
回答流程图中的两个问题:
- 问题1:因为都是由fisher引入book,一个模块只会引入另一个模块一次。所以只执行了一次book
- 问题2:由于一次是主流程执行fisher文件;一次是由book模块导入 fisher。
5.找不到视图函数的最终解释和证明
整个流程中,出现了两次核心app对象的初始化,注册路由是在蓝色流程中初始化的app注册的。
但是启动服务是红色流程中的app启动的
book中注册路由所使用的app对象,是他自己所导入fisher模块的app对象(蓝色流程中),
而不是红色主流程中所实例化的app对象
加入一些日志出数验证我们的结论。
我们在app实例化,启动,注册路由是哪个地方加入日志信息,来观察一下
book.py
1 | print("id为"+str(id(app))+"的app注册路由") |
fisher.py
1 | app = Flask(__name__) |
执行结果
1 | pydev debugger: process 63816 is connecting |
可以看到注册路由的app,和启动服务的app不是同一个app。
并且最后启动的app是最先实例化的app,也就是红色主流程的app;
而注册路由的app是后实例化的app,也就是由book导入fisher模块的蓝色流程的app